home *** CD-ROM | disk | FTP | other *** search
/ Stone Design / Stone Design.iso / Stone_Friends / Wave / WavesWorld / Source / Libraries / tcl7.4b3 / tclCkalloc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-01-03  |  16.8 KB  |  599 lines

  1. /* 
  2.  * tclCkalloc.c --
  3.  *
  4.  *    Interface to malloc and free that provides support for debugging problems
  5.  *    involving overwritten, double freeing memory and loss of memory.
  6.  *
  7.  * Copyright (c) 1991-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * This code contributed by Karl Lehenbauer and Mark Diekhans
  14.  *
  15.  */
  16.  
  17. static char sccsid[] = "@(#) tclCkalloc.c 1.5 95/01/03 17:05:22";
  18.  
  19. #include "tclInt.h"
  20.  
  21. #define FALSE    0
  22. #define TRUE    1
  23.  
  24. #ifdef TCL_MEM_DEBUG
  25. #ifndef TCL_GENERIC_ONLY
  26. #include "tclPort.h"
  27. #endif
  28.  
  29. #define GUARD_SIZE 8
  30.  
  31. struct mem_header {
  32.         long               length;
  33.         char              *file;
  34.         int                line;
  35.         struct mem_header *flink;
  36.         struct mem_header *blink;
  37.     int           dummy;    /* Aligns body on 8-byte boundary. */
  38.         unsigned char      low_guard[GUARD_SIZE];
  39.         char               body[1];
  40. };
  41.  
  42. static struct mem_header *allocHead = NULL;  /* List of allocated structures */
  43.  
  44. #define GUARD_VALUE  0341
  45.  
  46. /* static char high_guard[] = {0x89, 0xab, 0xcd, 0xef}; */
  47.  
  48. static int total_mallocs = 0;
  49. static int total_frees = 0;
  50. static int current_bytes_malloced = 0;
  51. static int maximum_bytes_malloced = 0;
  52. static int current_malloc_packets = 0;
  53. static int maximum_malloc_packets = 0;
  54. static int break_on_malloc = 0;
  55. static int trace_on_at_malloc = 0;
  56. static int  alloc_tracing = FALSE;
  57. static int  init_malloced_bodies = TRUE;
  58. #ifdef MEM_VALIDATE
  59.     static int  validate_memory = TRUE;
  60. #else
  61.     static int  validate_memory = FALSE;
  62. #endif
  63.  
  64. /*
  65.  * Prototypes for procedures defined in this file:
  66.  */
  67.  
  68. static int        MemoryCmd _ANSI_ARGS_((ClientData clientData,
  69.                 Tcl_Interp *interp, int argc, char **argv));
  70.  
  71. /*
  72.  *----------------------------------------------------------------------
  73.  *
  74.  * dump_memory_info --
  75.  *     Display the global memory management statistics.
  76.  *
  77.  *----------------------------------------------------------------------
  78.  */
  79. static void
  80. dump_memory_info(outFile) 
  81.     FILE *outFile;
  82. {
  83.         fprintf(outFile,"total mallocs             %10d\n", 
  84.                 total_mallocs);
  85.         fprintf(outFile,"total frees               %10d\n", 
  86.                 total_frees);
  87.         fprintf(outFile,"current packets allocated %10d\n", 
  88.                 current_malloc_packets);
  89.         fprintf(outFile,"current bytes allocated   %10d\n", 
  90.                 current_bytes_malloced);
  91.         fprintf(outFile,"maximum packets allocated %10d\n", 
  92.                 maximum_malloc_packets);
  93.         fprintf(outFile,"maximum bytes allocated   %10d\n", 
  94.                 maximum_bytes_malloced);
  95. }
  96.  
  97. /*
  98.  *----------------------------------------------------------------------
  99.  *
  100.  * ValidateMemory --
  101.  *     Procedure to validate allocted memory guard zones.
  102.  *
  103.  *----------------------------------------------------------------------
  104.  */
  105. static void
  106. ValidateMemory (memHeaderP, file, line, nukeGuards)
  107.     struct mem_header *memHeaderP;
  108.     char              *file;
  109.     int                line;
  110.     int                nukeGuards;
  111. {
  112.     unsigned char *hiPtr;
  113.     int   idx;
  114.     int   guard_failed = FALSE;
  115.     int byte;
  116.     
  117.     for (idx = 0; idx < GUARD_SIZE; idx++) {
  118.         byte = *(memHeaderP->low_guard + idx);
  119.         if (byte != GUARD_VALUE) {
  120.             guard_failed = TRUE;
  121.             fflush (stdout);
  122.         byte &= 0xff;
  123.             fprintf(stderr, "low guard byte %d is 0x%x  \t%c\n", idx, byte,
  124.             (isprint(UCHAR(byte)) ? byte : ' '));
  125.         }
  126.     }
  127.     if (guard_failed) {
  128.         dump_memory_info (stderr);
  129.         fprintf (stderr, "low guard failed at %lx, %s %d\n",
  130.                  (long unsigned int) memHeaderP->body, file, line);
  131.         fflush (stderr);  /* In case name pointer is bad. */
  132.         fprintf (stderr, "%ld bytes allocated at (%s %d)\n", memHeaderP->length,
  133.         memHeaderP->file, memHeaderP->line);
  134.         panic ("Memory validation failure");
  135.     }
  136.  
  137.     hiPtr = (unsigned char *)memHeaderP->body + memHeaderP->length;
  138.     for (idx = 0; idx < GUARD_SIZE; idx++) {
  139.         byte = *(hiPtr + idx);
  140.         if (byte != GUARD_VALUE) {
  141.             guard_failed = TRUE;
  142.             fflush (stdout);
  143.         byte &= 0xff;
  144.             fprintf(stderr, "hi guard byte %d is 0x%x  \t%c\n", idx, byte,
  145.             (isprint(UCHAR(byte)) ? byte : ' '));
  146.         }
  147.     }
  148.  
  149.     if (guard_failed) {
  150.         dump_memory_info (stderr);
  151.         fprintf (stderr, "high guard failed at %lx, %s %d\n",
  152.                  (long unsigned int) memHeaderP->body, file, line);
  153.         fflush (stderr);  /* In case name pointer is bad. */
  154.         fprintf (stderr, "%ld bytes allocated at (%s %d)\n",
  155.         memHeaderP->length, memHeaderP->file,
  156.         memHeaderP->line);
  157.         panic ("Memory validation failure");
  158.     }
  159.  
  160.     if (nukeGuards) {
  161.         memset ((char *) memHeaderP->low_guard, 0, GUARD_SIZE); 
  162.         memset ((char *) hiPtr, 0, GUARD_SIZE); 
  163.     }
  164.  
  165. }
  166.  
  167. /*
  168.  *----------------------------------------------------------------------
  169.  *
  170.  * Tcl_ValidateAllMemory --
  171.  *     Validates guard regions for all allocated memory.
  172.  *
  173.  *----------------------------------------------------------------------
  174.  */
  175. void
  176. Tcl_ValidateAllMemory (file, line)
  177.     char  *file;
  178.     int    line;
  179. {
  180.     struct mem_header *memScanP;
  181.  
  182.     for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink)
  183.         ValidateMemory (memScanP, file, line, FALSE);
  184.  
  185. }
  186.  
  187. /*
  188.  *----------------------------------------------------------------------
  189.  *
  190.  * Tcl_DumpActiveMemory --
  191.  *     Displays all allocated memory to stderr.
  192.  *
  193.  * Results:
  194.  *     Return TCL_ERROR if an error accessing the file occures, `errno' 
  195.  *     will have the file error number left in it.
  196.  *----------------------------------------------------------------------
  197.  */
  198. int
  199. Tcl_DumpActiveMemory (fileName)
  200.     char *fileName;
  201. {
  202.     FILE              *fileP;
  203.     struct mem_header *memScanP;
  204.     char              *address;
  205.  
  206.     fileP = fopen (fileName, "w");
  207.     if (fileP == NULL)
  208.         return TCL_ERROR;
  209.  
  210.     for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink) {
  211.         address = &memScanP->body [0];
  212.         fprintf (fileP, "%8lx - %8lx  %7ld @ %s %d",
  213.         (long unsigned int) address,
  214.                  (long unsigned int) address + memScanP->length - 1,
  215.          memScanP->length, memScanP->file, memScanP->line);
  216.     (void) fputc('\n', fileP);
  217.     }
  218.     fclose (fileP);
  219.     return TCL_OK;
  220. }
  221.  
  222. /*
  223.  *----------------------------------------------------------------------
  224.  *
  225.  * Tcl_DbCkalloc - debugging ckalloc
  226.  *
  227.  *        Allocate the requested amount of space plus some extra for
  228.  *        guard bands at both ends of the request, plus a size, panicing 
  229.  *        if there isn't enough space, then write in the guard bands
  230.  *        and return the address of the space in the middle that the
  231.  *        user asked for.
  232.  *
  233.  *        The second and third arguments are file and line, these contain
  234.  *        the filename and line number corresponding to the caller.
  235.  *        These are sent by the ckalloc macro; it uses the preprocessor
  236.  *        autodefines __FILE__ and __LINE__.
  237.  *
  238.  *----------------------------------------------------------------------
  239.  */
  240. char *
  241. Tcl_DbCkalloc(size, file, line)
  242.     unsigned int size;
  243.     char        *file;
  244.     int          line;
  245. {
  246.     struct mem_header *result;
  247.  
  248.     if (validate_memory)
  249.         Tcl_ValidateAllMemory (file, line);
  250.  
  251.     result = (struct mem_header *)malloc((unsigned)size + 
  252.                               sizeof(struct mem_header) + GUARD_SIZE);
  253.     if (result == NULL) {
  254.         fflush(stdout);
  255.         dump_memory_info(stderr);
  256.         panic("unable to alloc %d bytes, %s line %d", size, file, 
  257.               line);
  258.     }
  259.  
  260.     /*
  261.      * Fill in guard zones and size.  Also initialize the contents of
  262.      * the block with bogus bytes to detect uses of initialized data.
  263.      * Link into allocated list.
  264.      */
  265.     if (init_malloced_bodies) {
  266.         memset ((VOID *) result, GUARD_VALUE,
  267.         size + sizeof(struct mem_header) + GUARD_SIZE);
  268.     } else {
  269.     memset ((char *) result->low_guard, GUARD_VALUE, GUARD_SIZE);
  270.     memset (result->body + size, GUARD_VALUE, GUARD_SIZE);
  271.     }
  272.     result->length = size;
  273.     result->file = file;
  274.     result->line = line;
  275.     result->flink = allocHead;
  276.     result->blink = NULL;
  277.     if (allocHead != NULL)
  278.         allocHead->blink = result;
  279.     allocHead = result;
  280.  
  281.     total_mallocs++;
  282.     if (trace_on_at_malloc && (total_mallocs >= trace_on_at_malloc)) {
  283.         (void) fflush(stdout);
  284.         fprintf(stderr, "reached malloc trace enable point (%d)\n",
  285.                 total_mallocs);
  286.         fflush(stderr);
  287.         alloc_tracing = TRUE;
  288.         trace_on_at_malloc = 0;
  289.     }
  290.  
  291.     if (alloc_tracing)
  292.         fprintf(stderr,"ckalloc %lx %d %s %d\n",
  293.         (long unsigned int) result->body, size, file, line);
  294.  
  295.     if (break_on_malloc && (total_mallocs >= break_on_malloc)) {
  296.         break_on_malloc = 0;
  297.         (void) fflush(stdout);
  298.         fprintf(stderr,"reached malloc break limit (%d)\n", 
  299.                 total_mallocs);
  300.         fprintf(stderr, "program will now enter C debugger\n");
  301.         (void) fflush(stderr);
  302.     abort();
  303.     }
  304.  
  305.     current_malloc_packets++;
  306.     if (current_malloc_packets > maximum_malloc_packets)
  307.         maximum_malloc_packets = current_malloc_packets;
  308.     current_bytes_malloced += size;
  309.     if (current_bytes_malloced > maximum_bytes_malloced)
  310.         maximum_bytes_malloced = current_bytes_malloced;
  311.  
  312.     return result->body;
  313. }
  314.  
  315. /*
  316.  *----------------------------------------------------------------------
  317.  *
  318.  * Tcl_DbCkfree - debugging ckfree
  319.  *
  320.  *        Verify that the low and high guards are intact, and if so
  321.  *        then free the buffer else panic.
  322.  *
  323.  *        The guards are erased after being checked to catch duplicate
  324.  *        frees.
  325.  *
  326.  *        The second and third arguments are file and line, these contain
  327.  *        the filename and line number corresponding to the caller.
  328.  *        These are sent by the ckfree macro; it uses the preprocessor
  329.  *        autodefines __FILE__ and __LINE__.
  330.  *
  331.  *----------------------------------------------------------------------
  332.  */
  333.  
  334. int
  335. Tcl_DbCkfree(ptr, file, line)
  336.     char *  ptr;
  337.     char     *file;
  338.     int       line;
  339. {
  340.     struct mem_header *memp = 0;  /* Must be zero for size calc */
  341.  
  342.     /*
  343.      * Since header ptr is zero, body offset will be size
  344.      */
  345. #ifdef _CRAYCOM
  346.     memp = (struct mem_header *)((char *) ptr  - (sizeof(int)*((unsigned)&(memp->body))));
  347. #else
  348.     memp = (struct mem_header *)(((char *) ptr) - (int)memp->body);
  349. #endif
  350.  
  351.     if (alloc_tracing)
  352.         fprintf(stderr, "ckfree %lx %ld %s %d\n",
  353.         (long unsigned int) memp->body, memp->length, file, line);
  354.  
  355.     if (validate_memory)
  356.         Tcl_ValidateAllMemory (file, line);
  357.  
  358.     ValidateMemory (memp, file, line, TRUE);
  359.     if (init_malloced_bodies) {
  360.     memset((VOID *) ptr, GUARD_VALUE, (size_t) memp->length);
  361.     }
  362.  
  363.     total_frees++;
  364.     current_malloc_packets--;
  365.     current_bytes_malloced -= memp->length;
  366.  
  367.     /*
  368.      * Delink from allocated list
  369.      */
  370.     if (memp->flink != NULL)
  371.         memp->flink->blink = memp->blink;
  372.     if (memp->blink != NULL)
  373.         memp->blink->flink = memp->flink;
  374.     if (allocHead == memp)
  375.         allocHead = memp->flink;
  376.     free((char *) memp);
  377.     return 0;
  378. }
  379.  
  380. /*
  381.  *--------------------------------------------------------------------
  382.  *
  383.  * Tcl_DbCkrealloc - debugging ckrealloc
  384.  *
  385.  *    Reallocate a chunk of memory by allocating a new one of the
  386.  *    right size, copying the old data to the new location, and then
  387.  *    freeing the old memory space, using all the memory checking
  388.  *    features of this package.
  389.  *
  390.  *--------------------------------------------------------------------
  391.  */
  392. char *
  393. Tcl_DbCkrealloc(ptr, size, file, line)
  394.     char *ptr;
  395.     unsigned int size;
  396.     char *file;
  397.     int line;
  398. {
  399.     char *new;
  400.     unsigned int copySize;
  401.     struct mem_header *memp = 0;  /* Must be zero for size calc */
  402.  
  403. #ifdef _CRAYCOM
  404.     memp = (struct mem_header *)((char *) ptr  - (sizeof(int)*((unsigned)&(memp->body))));
  405. #else
  406.     memp = (struct mem_header *)(((char *) ptr) - (int)memp->body);
  407. #endif
  408.     copySize = size;
  409.     if (copySize > memp->length) {
  410.     copySize = memp->length;
  411.     }
  412.     new = Tcl_DbCkalloc(size, file, line);
  413.     memcpy((VOID *) new, (VOID *) ptr, (size_t) copySize);
  414.     Tcl_DbCkfree(ptr, file, line);
  415.     return(new);
  416. }
  417.  
  418. /*
  419.  *----------------------------------------------------------------------
  420.  *
  421.  * MemoryCmd --
  422.  *     Implements the TCL memory command:
  423.  *       memory info
  424.  *       memory display
  425.  *       break_on_malloc count
  426.  *       trace_on_at_malloc count
  427.  *       trace on|off
  428.  *       validate on|off
  429.  *
  430.  * Results:
  431.  *     Standard TCL results.
  432.  *
  433.  *----------------------------------------------------------------------
  434.  */
  435.     /* ARGSUSED */
  436. static int
  437. MemoryCmd (clientData, interp, argc, argv)
  438.     ClientData  clientData;
  439.     Tcl_Interp *interp;
  440.     int         argc;
  441.     char      **argv;
  442. {
  443.     char *fileName;
  444.     Tcl_DString buffer;
  445.     int result;
  446.  
  447.     if (argc < 2) {
  448.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  449.         argv[0], " option [args..]\"", (char *) NULL);
  450.     return TCL_ERROR;
  451.     }
  452.  
  453.     if (strcmp(argv[1],"trace") == 0) {
  454.         if (argc != 3) 
  455.             goto bad_suboption;
  456.         alloc_tracing = (strcmp(argv[2],"on") == 0);
  457.         return TCL_OK;
  458.     }
  459.     if (strcmp(argv[1],"init") == 0) {
  460.         if (argc != 3)
  461.             goto bad_suboption;
  462.         init_malloced_bodies = (strcmp(argv[2],"on") == 0);
  463.         return TCL_OK;
  464.     }
  465.     if (strcmp(argv[1],"validate") == 0) {
  466.         if (argc != 3)
  467.              goto bad_suboption;
  468.         validate_memory = (strcmp(argv[2],"on") == 0);
  469.         return TCL_OK;
  470.     }
  471.     if (strcmp(argv[1],"trace_on_at_malloc") == 0) {
  472.         if (argc != 3) 
  473.             goto argError;
  474.         if (Tcl_GetInt(interp, argv[2], &trace_on_at_malloc) != TCL_OK)
  475.                 return TCL_ERROR;
  476.          return TCL_OK;
  477.     }
  478.     if (strcmp(argv[1],"break_on_malloc") == 0) {
  479.         if (argc != 3) 
  480.             goto argError;
  481.         if (Tcl_GetInt(interp, argv[2], &break_on_malloc) != TCL_OK)
  482.                 return TCL_ERROR;
  483.         return TCL_OK;
  484.     }
  485.  
  486.     if (strcmp(argv[1],"info") == 0) {
  487.         dump_memory_info(stdout);
  488.         return TCL_OK;
  489.     }
  490.     if (strcmp(argv[1],"active") == 0) {
  491.         if (argc != 3) {
  492.         Tcl_AppendResult(interp, "wrong # args:  should be \"",
  493.             argv[0], " active file", (char *) NULL);
  494.         return TCL_ERROR;
  495.     }
  496.     fileName = Tcl_TildeSubst(interp, argv[2], &buffer);
  497.     if (fileName == NULL) {
  498.         return TCL_ERROR;
  499.     }
  500.     result = Tcl_DumpActiveMemory (fileName);
  501.     Tcl_DStringFree(&buffer);
  502.     if (result != TCL_OK) {
  503.         Tcl_AppendResult(interp, "error accessing ", argv[2], 
  504.             (char *) NULL);
  505.         return TCL_ERROR;
  506.     }
  507.     return TCL_OK;
  508.     }
  509.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  510.         "\":  should be info, init, active, break_on_malloc, ",
  511.         "trace_on_at_malloc, trace, or validate", (char *) NULL);
  512.     return TCL_ERROR;
  513.  
  514. argError:
  515.     Tcl_AppendResult(interp, "wrong # args:  should be \"", argv[0],
  516.         " ", argv[1], "count\"", (char *) NULL);
  517.     return TCL_ERROR;
  518.  
  519. bad_suboption:
  520.     Tcl_AppendResult(interp, "wrong # args:  should be \"", argv[0],
  521.         " ", argv[1], " on|off\"", (char *) NULL);
  522.     return TCL_ERROR;
  523. }
  524.  
  525. /*
  526.  *----------------------------------------------------------------------
  527.  *
  528.  * Tcl_InitMemory --
  529.  *     Initialize the memory command.
  530.  *
  531.  *----------------------------------------------------------------------
  532.  */
  533. void
  534. Tcl_InitMemory(interp)
  535.     Tcl_Interp *interp;
  536. {
  537. Tcl_CreateCommand (interp, "memory", MemoryCmd, (ClientData) NULL, 
  538.                   (Tcl_CmdDeleteProc *) NULL);
  539. }
  540.  
  541. #else
  542.  
  543.  
  544. /*
  545.  *----------------------------------------------------------------------
  546.  *
  547.  * Tcl_Ckalloc --
  548.  *     Interface to malloc when TCL_MEM_DEBUG is disabled.  It does check
  549.  *     that memory was actually allocated.
  550.  *
  551.  *----------------------------------------------------------------------
  552.  */
  553. VOID *
  554. Tcl_Ckalloc (size)
  555.     unsigned int size;
  556. {
  557.         char *result;
  558.  
  559.         result = malloc(size);
  560.         if (result == NULL) 
  561.                 panic("unable to alloc %d bytes", size);
  562.         return result;
  563. }
  564.  
  565. /*
  566.  *----------------------------------------------------------------------
  567.  *
  568.  * TckCkfree --
  569.  *     Interface to free when TCL_MEM_DEBUG is disabled.  Done here rather
  570.  *     in the macro to keep some modules from being compiled with 
  571.  *     TCL_MEM_DEBUG enabled and some with it disabled.
  572.  *
  573.  *----------------------------------------------------------------------
  574.  */
  575. void
  576. Tcl_Ckfree (ptr)
  577.     VOID *ptr;
  578. {
  579.         free (ptr);
  580. }
  581.  
  582. /*
  583.  *----------------------------------------------------------------------
  584.  *
  585.  * Tcl_InitMemory --
  586.  *     Dummy initialization for memory command, which is only available 
  587.  *     if TCL_MEM_DEBUG is on.
  588.  *
  589.  *----------------------------------------------------------------------
  590.  */
  591.     /* ARGSUSED */
  592. void
  593. Tcl_InitMemory(interp)
  594.     Tcl_Interp *interp;
  595. {
  596. }
  597.  
  598. #endif
  599.